package master.common.utils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.TreeMap;
import java.util.WeakHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ClassUtils;

/**
 * 其实反射工具比如Apache的Beanutils已经是一个狠强大的工具。
 * 这里班门弄斧一把，主要是为了俺的代码不会依赖他的东东
 *
 */
public class PojoUtils {
	private static final Logger logger = LoggerFactory.getLogger(PojoUtils.class);
	/**
	 * 类和属性名组合到属性描述的映射
	 */
	private static Map<Class<?>, Map<String, PropertyDescriptor>> clsandProp2propertyDescMap = new WeakHashMap<Class<?>, Map<String, PropertyDescriptor>>();
	/**
	 * 类到属性名数组的映射
	 */
	private static Map<Class<?>, String[]> cls2propertiesMap = new WeakHashMap<Class<?>, String[]>();
	
	/**
	 * 获取某类型的属性数组
	 * @param cls
	 * @return
	 */
	public synchronized static String[] getProperties(Class<?> cls){
		if (!cls2propertiesMap.containsKey(cls)) {
			initClass(cls);
		}
		return cls2propertiesMap.get(cls);
	}
	
	/**
	 * 获取某类型指定属性的描述
	 * @param cls
	 * @param propertyName
	 * @return 返回该属性描述，如果此类型不存在该属性，则返回null
	 */
	public synchronized static PropertyDescriptor getPropertyDesc(Class<?> cls, String propertyName){
		if (!clsandProp2propertyDescMap.containsKey(cls)) {
			initClass(cls);
		}
		return clsandProp2propertyDescMap.get(cls).get(propertyName);
	}
	
	/**
	 * 设置某个对象指定属性的值
	 * @param dest
	 * @param propName
	 * @param propValue
	 */
	public static void setProperty(Object dest, String propName, Object propValue){
		try {
			PropertyDescriptor propertyDescriptor = getPropertyDesc(dest.getClass(), propName);
			if(propertyDescriptor != null && propertyDescriptor.getWriteMethod() != null) {
				if (!ClassUtils.isAssignableValue(propertyDescriptor.getPropertyType(), propValue)) {
					propValue = ObjectUtils.convertValue(propValue, propertyDescriptor.getPropertyType());
				}
				propertyDescriptor.getWriteMethod().invoke(dest, propValue);
			}
		} catch (Exception e) {
			logger.error("设置属性("+propName+")出错", e);
		}
	}
	
	/**
	 * 获取某个对象指定属性的值
	 * @param src
	 * @param propName
	 * @return 该对象的属性值
	 */
	public static Object getProperty(Object src, String propName){
		try {
			PropertyDescriptor propertyDescriptor = getPropertyDesc(src.getClass(), propName);
			return (propertyDescriptor==null || propertyDescriptor.getReadMethod() == null)?
					null:propertyDescriptor.getReadMethod().invoke(src);
		} catch (Exception e) {
			return null;
		}
	}
	
	private static void initClass(Class<?> cls){
		try {
			BeanInfo beanInfo = Introspector.getBeanInfo(cls);
			PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
			Map<String, PropertyDescriptor> propDescMap = new TreeMap<String, PropertyDescriptor>();
			for (int i=0; i< propertyDescriptors.length; i++) {
				PropertyDescriptor propertyDescriptor = propertyDescriptors[i];
				Method readMethod = propertyDescriptor.getReadMethod();
				if (readMethod != null && readMethod.getDeclaringClass().equals(Object.class) ) {
					continue;
				}
				propDescMap.put(propertyDescriptor.getName(), propertyDescriptor);
			}
			String[] properties = propDescMap.keySet().toArray(new String[propDescMap.size()]);
			cls2propertiesMap.put(cls, properties);
			clsandProp2propertyDescMap.put(cls, propDescMap);
		} catch (Exception e) {
			//如果反射出了错，我即便是求爷爷告奶奶也没有用，这个异常啊，打折的牙齿往肚里吞算了。
			cls2propertiesMap.put(cls, new String[0]);
		}
	}
}